﻿/*
 * Author: Brian Brewder
 * Website: http://redwerb.com
 */

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Redwerb.Visualizers.Controls
{
    public partial class ImageFrame : UserControl
    {

        #region Initialization and Destruction

        public ImageFrame()
        {
            InitializeComponent();
        }

        #endregion

        #region Fields and Properties

        public Image Image
        {
            get { return picImage.Image; }
            set
            {
                if (picImage.Image == value) return;
                picImage.Image = value;
                SetPictureBoxSize();
                OnImageChanged(EventArgs.Empty);
            }
        }

        private bool mImageBorder = false;
        [Category("Appearance")]
        [Description("Displays a border around the image.")]
        [DefaultValue(false)]
        public bool ImageBorder
        {
            get { return mImageBorder; }
            set
            {
                if (mImageBorder == value) return;
                mImageBorder = value;
                Refresh();
                OnImageBorderChanged(EventArgs.Empty);
            }
        }

        private Color mImageBorderColor = Color.White;
        [Category("Appearance")]
        [Description("The color of the border around the image.")]
        [DefaultValue(typeof(Color), "White")]
        public Color ImageBorderColor
        {
            get { return mImageBorderColor; }
            set
            {
                if (mImageBorderColor == value) return;
                mImageBorderColor = value;
                Refresh();
                OnImageBorderColorChanged(EventArgs.Empty);
            }
        }

        /// <summary>
        /// Gets the location of the image within the frame.
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false)]
        public Point ImageLocation
        {
            get { return picImage.Location; }
            set
            {
                var location = new Point(value.X, value.Y);
                var pad = this.Padding;
                var center = new Point((this.Width - picImage.Width) / 2, (this.Height - picImage.Height) / 2);
                var min = new Point(this.Width - picImage.Width - this.Padding.Right, this.Height - picImage.Height - this.Padding.Bottom);
                var max = new Point(pad.Left, pad.Top);

                if (picImage.Width < this.Width - pad.Left - pad.Right)
                {
                    location.X = center.X;
                }
                else
                {
                    location.X = Math.Min(max.X, location.X);
                    location.X = Math.Max(min.X, location.X);
                }

                if (picImage.Height < this.Height - pad.Top - pad.Bottom)
                {
                    location.Y = center.Y;
                }
                else
                {
                    location.Y = Math.Min(max.Y, location.Y);
                    location.Y = Math.Max(min.Y, location.Y);
                }

                picImage.Location = location;
                Application.DoEvents();
            }
        }

        /// <summary>
        /// Gets the size of the image as displayed to the user.
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false)]
        public Size ImageDisplaySize
        {
            get { return picImage.Size; }
        }

        private float mImageScale = 1f;
        [Category("Behavior")]
        [Description("A value that determines how large to display the image. A value of 1 is 100%, 2 is 200%, 0 will hide the image.")]
        [DefaultValue(1f)]
        public float ImageScale
        {
            get { return mImageScale; }
            set
            {
                if (mImageScale == value) return;
                mImageScale = value;
                SetPictureBoxSize();
                OnImageScaleChanged(EventArgs.Empty);
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Centers the image around the given point.
        /// </summary>
        /// <param name="center">The point that should be in the center of the frame.</param>
        public void CenterImage(Point center)
        {
            var x = (this.Width / 2) - center.X;
            var y = (this.Height / 2) - center.Y;
            ImageLocation = new Point(x, y);
        }

        /// <summary>
        /// Centers the image around the given image area. Useful for zomming in and out (size changes therefore Point changes).
        /// </summary>
        /// <param name="horizontal">A value between 0 and 1 that gives the horizontal distance from the left in a percent. 0 means left, 1 means right, .5 means half way.</param>
        /// <param name="vertical">A value between 0 and 1 that gives the vertical distance from the top in a percent. 0 means top, 1 means bottom, .5 means half way.</param>
        public void CenterImage(float horizontal, float vertical)
        {
            var x = (int)(picImage.Width * horizontal);
            var y = (int)(picImage.Height * vertical);
            CenterImage(new Point(x, y));
        }

        private void SetPictureBoxSize()
        {
            if (Image == null) return;

            //todo: Find the center point.
            // Change the size around the center.
            var x = (this.Width / 2) - picImage.Left;
            var y = (this.Height / 2) - picImage.Top;
            var horizontal = (float)x / (float)picImage.Width;
            var vertical = (float)y / (float)picImage.Height;
            picImage.Height = (int)(Image.Height * mImageScale);
            picImage.Width = (int)(Image.Width * mImageScale);
            CenterImage(horizontal, vertical);
        }

        public void BestFit()
        {
            ImageScale = GetBestFit();
            CenterImage(.5f, .5f);
        }

        public float GetBestFit()
        {
            int height = Image.Height;
            int width = Image.Width;

            float actualRatio = (float)width / (float)height;
            float maxRatio = (float)this.Width / (float)this.Height;
            float resizeRatio;
            int hPad = this.Padding.Left + this.Padding.Right;
            int vPad = this.Padding.Top + this.Padding.Bottom;

            if (actualRatio > maxRatio)
                // width is the determinate side.
                resizeRatio = (float)(this.Width - hPad) / (float)width;
            else
                // height is the determinate side.
                resizeRatio = (float)(this.Height - vPad) / (float)height;

            return resizeRatio;
        }

        #endregion

        #region Event Handlers

        private void picImage_Paint(object sender, PaintEventArgs e)
        {
            if (mImageBorder)
            {
                int thickness = 1;
                Pen p;
                p = new Pen(mImageBorderColor, thickness);
                e.Graphics.DrawRectangle(p, new Rectangle(thickness / 2, thickness / 2, picImage.Width - thickness, picImage.Height - thickness));
                p.Dispose();
            }
        }

        private MouseButtons mMouseButton = MouseButtons.None;
        private Point mStartLocation = Point.Empty;
        private void picImage_MouseDown(object sender, MouseEventArgs e)
        {
            mMouseButton = e.Button;
            mStartLocation = e.Location;
        }

        private void picImage_MouseUp(object sender, MouseEventArgs e)
        {
            mMouseButton = MouseButtons.None;
            mStartLocation = Point.Empty;
        }

        private void picImage_MouseMove(object sender, MouseEventArgs e)
        {
            if (mMouseButton != MouseButtons.Left) return;

            var pt = new Point(picImage.Left + (e.X - mStartLocation.X), picImage.Top + (e.Y - mStartLocation.Y));
            ImageLocation = pt;
        }

        private void picImage_LocationChanged(object sender, EventArgs e)
        {
            OnImageLocationChanged(EventArgs.Empty);
        }

        private void picImage_SizeChanged(object sender, EventArgs e)
        {
            OnImageDisplaySizeChanged(EventArgs.Empty);
        }

        private void ImageFrame_SizeChanged(object sender, EventArgs e)
        {
            // Ensures the location is still valid.
            ImageLocation = ImageLocation;
        }

        #endregion

        #region Events

        public delegate void ImageScaleChangedHandler(object sender, EventArgs e);
        [Category("Property Changed")]
        public event ImageScaleChangedHandler ImageScaleChanged;
        protected virtual void OnImageScaleChanged(EventArgs e)
        {
            if (ImageScaleChanged == null) return;
            ImageScaleChanged(this, e);
        }

        public delegate void ImageChangedHandler(object sender, EventArgs e);
        [Category("Property Changed")]
        public event ImageChangedHandler ImageChanged;
        protected virtual void OnImageChanged(EventArgs e)
        {
            if (ImageChanged == null) return;
            ImageChanged(this, e);
        }

        public delegate void ImageBorderChangedHandler(object sender, EventArgs e);
        [Category("Property Changed")]
        public event ImageBorderChangedHandler ImageBorderChanged;
        protected virtual void OnImageBorderChanged(EventArgs e)
        {
            if (ImageBorderChanged == null) return;
            ImageBorderChanged(this, e);
        }

        public delegate void ImageBorderColorChangedHandler(object sender, EventArgs e);
        [Category("Property Changed")]
        public event ImageBorderColorChangedHandler ImageBorderColorChanged;
        protected virtual void OnImageBorderColorChanged(EventArgs e)
        {
            if (ImageBorderColorChanged == null) return;
            ImageBorderColorChanged(this, e);
        }

        public delegate void ImageLocationChangedHandler(object sender, EventArgs e);
        [Category("Property Changed")]
        public event ImageLocationChangedHandler ImageLocationChanged;
        protected virtual void OnImageLocationChanged(EventArgs e)
        {
            if (ImageLocationChanged == null) return;
            ImageLocationChanged(this, e);
        }

        public delegate void ImageDisplaySizeChangedHandler(object sender, EventArgs e);
        [Category("Property Changed")]
        public event ImageDisplaySizeChangedHandler ImageDisplaySizeChanged;
        protected virtual void OnImageDisplaySizeChanged(EventArgs e)
        {
            if (ImageDisplaySizeChanged == null) return;
            ImageDisplaySizeChanged(this, e);
        }

        #endregion

    }
}
